package webhook

import (
	"context"
	"fmt"
	"net/http"
	"time"

	notificationv1 "github.com/devops-smartbot/devops-smartbot/proto/gen/go/notification/v1"
	"github.com/devops-smartbot/devops-smartbot/services/alert-service/internal/model"
	"github.com/devops-smartbot/devops-smartbot/services/alert-service/internal/repository"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

// AlertmanagerWebhook represents the Alertmanager webhook payload
type AlertmanagerWebhook struct {
	Version           string                 `json:"version"`
	GroupKey          string                 `json:"groupKey"`
	TruncatedAlerts   int                    `json:"truncatedAlerts"`
	Status            string                 `json:"status"`
	Receiver          string                 `json:"receiver"`
	GroupLabels       map[string]string      `json:"groupLabels"`
	CommonLabels      map[string]string      `json:"commonLabels"`
	CommonAnnotations map[string]string      `json:"commonAnnotations"`
	ExternalURL       string                 `json:"externalURL"`
	Alerts            []AlertmanagerAlert    `json:"alerts"`
}

type AlertmanagerAlert struct {
	Status       string            `json:"status"`
	Labels       map[string]string `json:"labels"`
	Annotations  map[string]string `json:"annotations"`
	StartsAt     time.Time         `json:"startsAt"`
	EndsAt       time.Time         `json:"endsAt"`
	GeneratorURL string            `json:"generatorURL"`
	Fingerprint  string            `json:"fingerprint"`
}

// Handler handles incoming Alertmanager webhooks
type Handler struct {
	repo             repository.Repository
	logger           *zap.Logger
	notificationConn *grpc.ClientConn
	notificationClient notificationv1.NotificationServiceClient
}

// NewHandler creates a new webhook handler
func NewHandler(repo repository.Repository, logger *zap.Logger, notificationAddr string) *Handler {
	h := &Handler{
		repo:   repo,
		logger: logger,
	}

	// Connect to notification service
	if notificationAddr != "" {
		conn, err := grpc.NewClient(notificationAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
		if err != nil {
			logger.Error("Failed to connect to notification service", zap.Error(err))
		} else {
			h.notificationConn = conn
			h.notificationClient = notificationv1.NewNotificationServiceClient(conn)
			logger.Info("Connected to notification service", zap.String("addr", notificationAddr))
		}
	}

	return h
}

// Close closes the notification service connection
func (h *Handler) Close() {
	if h.notificationConn != nil {
		h.notificationConn.Close()
	}
}

// HandleAlertmanagerWebhook processes incoming Alertmanager webhooks
func (h *Handler) HandleAlertmanagerWebhook(c *gin.Context) {
	var webhook AlertmanagerWebhook
	if err := c.ShouldBindJSON(&webhook); err != nil {
		h.logger.Error("Failed to parse webhook", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid webhook payload"})
		return
	}

	h.logger.Info("Received Alertmanager webhook",
		zap.String("receiver", webhook.Receiver),
		zap.String("status", webhook.Status),
		zap.Int("alertCount", len(webhook.Alerts)))

	ctx := context.Background()

	// Process each alert
	for _, amAlert := range webhook.Alerts {
		alert := convertToAlert(amAlert)

		// Check if alert already exists by fingerprint
		existingAlert, err := h.repo.GetAlertByFingerprint(ctx, alert.Fingerprint)
		if err == nil && existingAlert != nil {
			// Update existing alert
			existingAlert.Status = alert.Status
			existingAlert.EndsAt = alert.EndsAt
			existingAlert.UpdatedAt = time.Now()

			if err := h.repo.UpdateAlert(ctx, existingAlert); err != nil {
				h.logger.Error("Failed to update alert", zap.Error(err), zap.String("fingerprint", alert.Fingerprint))
			} else {
				h.logger.Info("Updated alert", zap.String("fingerprint", alert.Fingerprint), zap.String("status", alert.Status))
			}
		} else {
			// Create new alert with generated ID
			alert.ID = generateAlertID()
			alert.CreatedAt = time.Now()
			alert.UpdatedAt = time.Now()

			if err := h.repo.CreateAlert(ctx, alert); err != nil {
				h.logger.Error("Failed to create alert", zap.Error(err), zap.String("fingerprint", alert.Fingerprint))
			} else {
				h.logger.Info("Created new alert", zap.String("id", alert.ID), zap.String("fingerprint", alert.Fingerprint), zap.String("alertname", alert.Alertname))

				// Send notification for new alert
				h.sendAlertNotification(ctx, alert)
			}
		}
	}

	c.JSON(http.StatusOK, gin.H{"message": "Webhook processed successfully"})
}

// generateAlertID generates a unique alert ID
func generateAlertID() string {
	return uuid.New().String()
}

// sendAlertNotification sends a notification for a new alert
func (h *Handler) sendAlertNotification(ctx context.Context, alert *model.Alert) {
	if h.notificationClient == nil {
		return
	}

	// Determine priority based on severity
	priority := notificationv1.Priority_PRIORITY_MEDIUM
	switch alert.Severity {
	case "critical":
		priority = notificationv1.Priority_PRIORITY_CRITICAL
	case "warning":
		priority = notificationv1.Priority_PRIORITY_HIGH
	case "info":
		priority = notificationv1.Priority_PRIORITY_LOW
	}

	// Build notification subject and body
	subject := fmt.Sprintf("🚨 Alert: %s [%s]", alert.Alertname, alert.Severity)
	body := fmt.Sprintf(`
Alert: %s
Severity: %s
Instance: %s
Description: %s
Started: %s
`, alert.Alertname, alert.Severity, alert.Instance, alert.Description, alert.StartsAt.Format(time.RFC3339))

	// Send email notification
	_, err := h.notificationClient.SendNotification(ctx, &notificationv1.SendNotificationRequest{
		Channel:    notificationv1.Channel_CHANNEL_EMAIL,
		Recipients: []string{"admin@example.com"}, // TODO: Get from config
		Subject:    subject,
		Body:       body,
		Priority:   priority,
		Source:     "alert",
		SourceId:   alert.ID,
	})

	if err != nil {
		h.logger.Error("Failed to send email notification", zap.Error(err), zap.String("alert_id", alert.ID))
	}

	// Send Slack notification
	_, err = h.notificationClient.SendNotification(ctx, &notificationv1.SendNotificationRequest{
		Channel:    notificationv1.Channel_CHANNEL_SLACK,
		Recipients: []string{"#alerts"}, // TODO: Get from config
		Subject:    subject,
		Body:       body,
		Priority:   priority,
		Source:     "alert",
		SourceId:   alert.ID,
	})

	if err != nil {
		h.logger.Error("Failed to send Slack notification", zap.Error(err), zap.String("alert_id", alert.ID))
	}
}

// convertToAlert converts Alertmanager alert to internal alert model
func convertToAlert(amAlert AlertmanagerAlert) *model.Alert {
	// Extract common fields from labels
	alertname := amAlert.Labels["alertname"]
	severity := amAlert.Labels["severity"]
	if severity == "" {
		severity = "medium" // default severity
	}
	instance := amAlert.Labels["instance"]
	job := amAlert.Labels["job"]

	// Extract description from annotations
	description := amAlert.Annotations["description"]
	if description == "" {
		description = amAlert.Annotations["summary"]
	}

	// Map Alertmanager status to internal status
	status := "active"
	if amAlert.Status == "resolved" {
		status = "resolved"
	}

	return &model.Alert{
		Alertname:   alertname,
		Severity:    severity,
		Status:      status,
		Instance:    instance,
		Job:         job,
		StartsAt:    amAlert.StartsAt,
		EndsAt:      amAlert.EndsAt,
		Description: description,
		Labels:      amAlert.Labels,
		Annotations: amAlert.Annotations,
		Fingerprint: amAlert.Fingerprint,
	}
}
